﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Windows.Forms;

namespace sim_dc
{

    // clasa compiler primeste de la parser un obiect de tip Parser:D 
    // care contine un Dictionar de <Instruction, Int32> adica instructiune (acolo ai opcode, mod de
    // adresare, operanzi, ce tip e clasa - si adresa la care se gaseste in prog)
    // si un dictionar pt zona de date cu numele variabilelor si o lista valori-adresa pt fiecare 

    // List<Instruction> instructionList = new List<Instruction>();
    // Dictionary<String, Object> dataList = new Dictionary<String, Object>(); 
    // Object e de fapt List<ValueAddress> 

    // acum din datele astea tu trebuie sa le scri in binar in fisier sau un textBox
    /* 
     0000000000000004  ;zona de date, biti12-15 - id zona; b0-11 nr de cuvinte care urmeaza (4)
     0000000000000001   date 
     0000000000000003
     0000000000000005
     0000000000000007
     1111000000000000  ; zona de cod 0xF, ultimii 12 biti semnifica cate instructiuni sunt :) 
     0001010000010003
      ...              ; instructiuni
     
     1100110100000000  ; HALT
     
      
     .data
     n: 1
     m: 3,5,7
     .code
     add r0,r3
     ...
     halt
      
    // o sa fie pb la etichete ca inca nu stiu cum le codific :D
     * si la instructiunile cu op imediati si indexati acolo dupa instructiune mai trebuie codificata
     * si valoarea imediata sau indexul
     
     instructiunile si reg sunt codificati si in clasa encodings dc iti trebuie 
     
     */

    class Compiler
    {
        FileStream fs;
        BinaryWriter bw;
        Parser _parser;
        RichTextBox output = null;
        bool index1 = false;
        bool index2 = false;
        bool eOffset = false;
        int opImediat1 = 0;
        int opImediat2 = 0;
        int offset = 0;


        public Compiler(Parser p, RichTextBox output)
        {
            String path = "bla.bin";
            fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            bw = new BinaryWriter(fs);
            _parser = p;
            this.output = output;
            WriteData();
            WriteCode();
            CloseFile();

        }
        public void CloseFile()
        {
            bw.Close();
            fs.Close();

        }


        public void WriteHeader(ProgramSector p, int nr)
        {
            byte[] b;
            byte[] word = new byte[2];
            bool[] bits = new bool[16];
            System.Collections.IEnumerator ienum;
            b = System.BitConverter.GetBytes(nr);
            word[0] = b[0];
            word[1] = b[1];
            BitArray bit = new BitArray(word);
            ienum = bit.GetEnumerator();
            int k = 0;
            while (ienum.MoveNext())
            {
                bits[k++] = Convert.ToBoolean(ienum.Current);
            }

            //in bits am val lui nr 
            //pun antetul pt date sau cod
            for (int j = bits.Length - 1; j >= 12; j--)
            {
                if (p == ProgramSector.data) bits[j] = false;
                else bits[j] = true;
            }
            
            for (int j = bits.Length - 1; j >= 0; j--)
            {
               
                if (bits[j]) output.AppendText("1");
                else output.AppendText("0");
            }
            output.AppendText("\n");
            bw.Write(BoolToByte(bits));
            

        }
        public void WriteWord(int nr)
        {
           
            byte[] b;
            byte[] word = new byte[2];
            bool[] bits = new bool[16];
            System.Collections.IEnumerator ienum;
            b = System.BitConverter.GetBytes(nr);
            word[0] = b[0];
            word[1] = b[1];
            

            BitArray bit = new BitArray(word);
            ienum = bit.GetEnumerator();
            int k = 0;
            while (ienum.MoveNext())
            {
                bits[k++] = Convert.ToBoolean(ienum.Current);
            }

            for (int j = bits.Length - 1; j >= 0; j--)
            {
            
                if (bits[j]) output.AppendText("1");
                else output.AppendText("0");
            }
            output.AppendText("\n");
            bw.Write(BoolToByte(bits));
           // bw.Write(word);

        }
        public void WriteData()
        {
            output.AppendText("segmentul de date:\n");
            WriteHeader(ProgramSector.data, Parser.dataList.Count);
            foreach (KeyValuePair<string, List<ValueAddress> >kvp in Parser.dataList)
            {
                
                System.Collections.IEnumerator ienum;
                ienum = kvp.Value.GetEnumerator();
                while (ienum.MoveNext())
                {
                    ValueAddress v = new ValueAddress();
                    v = (ValueAddress)ienum.Current;
                    //output.AppendText("Valoarea ce urmeaza: " + v.GetValue() + "\n ");
                    WriteWord(v.GetValue());
                }
               
            }
        }
        public void WriteCode()
        {
            output.AppendText("segmentul de cod:\n");
            WriteHeader(ProgramSector.code, Parser.instructionList.Count);
            System.Collections.IEnumerator ienum;
            ienum = Parser.instructionList.GetEnumerator();
            while (ienum.MoveNext())
            {
                Instruction instr = new Instruction();
                instr = (Instruction)ienum.Current;
                CodificaInstructiune(instr);
            }
        }
        public void CodificaInstructiune(Instruction instr) 
        {
            BitArray cod = new BitArray(16);
            bool[] bits = new bool[16];
           // bool[] x = new bool[16];

            int opcode = instr.GetOpcode();
            byte[] b;
            byte[] word = new byte[2];
            //dintr-un int pe 32 de biti, opcodeul se afla pe ultimii 2 octeti
            
            b = System.BitConverter.GetBytes(opcode);
            word[0] = b[0];
            word[1] = b[1];
            System.Collections.IEnumerator ienum;
            BitArray bit = new BitArray(word);
            ienum = bit.GetEnumerator();
            int k = 0;
            while (ienum.MoveNext())
            {
                bits[k++] = Convert.ToBoolean(ienum.Current);
            }
            //in bits voi pune codificarea instructiune. Acum contine doar opcodeul si trebuie
            //adaugati operanzii, pe pozitiile corespunzatoare, in functie de clasa.

            if (instr.GetInstrType() == InstrType.B1)
                      cu2Op(instr, bits);
            if (instr.GetInstrType() == InstrType.B2) 
                      cu1Op(instr,bits);
            if (instr.GetInstrType() == InstrType.B3)
                      cuBranch(instr, bits);


            double nr = 0;
            for (int j = bits.Length - 1; j >= 0; j--)
            {
                bw.Write(bits[j]);
                if (bits[j]) 
                { 
                    output.AppendText("1");
                 
                }
                else output.AppendText("0");
                
            }
            output.AppendText("\n");
            bw.Write(BoolToByte(bits));
           
            if (index1) { /*output.AppendText("OperandImediat: ");*/ WriteWord(opImediat1); index1 = false; };
            if (index2) { /*output.AppendText("OperandImediat: ");*/ WriteWord(opImediat2); index2 = false; };
        }


        public void cu2Op(Instruction instr, bool[] bits)
        {
            System.Collections.IEnumerator ienum;
            Operand o = new Operand();
            o = instr.GetFirstOp();
            switch (o.GetModAdresare())
            {
                case ModAdresare.Imediat: bits[11] = bits[10] = false; index1 = true; opImediat1 = o.GetValImediata(); break;
                case ModAdresare.Indirect: bits[11] = true; bits[10] = false; break;
                case ModAdresare.Direct: bits[11] = false; bits[10] = true; break;
                case ModAdresare.Indexat: bits[11] = bits[10] = true; index1 = true; opImediat1 = o.GetValImediata(); break;
            }

            BitArray nrReg = new BitArray(System.BitConverter.GetBytes(o.Register()));
            ienum = nrReg.GetEnumerator();
            bool[] valoare = new bool[32];
            int a = 0;
            while (ienum.MoveNext())
            {
                valoare[a++] = Convert.ToBoolean(ienum.Current);
            }
            a = 0;
            for (int i = 6; i <= 9; i++)
            { bits[i] = valoare[a++]; }

            o = instr.GetLastOp();
            switch (o.GetModAdresare())
            {
                case ModAdresare.Imediat: bits[5] = bits[4] = false; index2 = true; opImediat2 = o.GetValImediata(); break;
                case ModAdresare.Indirect: bits[5] = true; bits[4] = false; break;
                case ModAdresare.Direct: bits[5] = false; bits[4] = true; break;
                case ModAdresare.Indexat: bits[5] = bits[4] = true; index2 = true; opImediat2 = o.GetValImediata(); break;
            }
            nrReg = new BitArray(System.BitConverter.GetBytes(o.Register()));
            ienum = nrReg.GetEnumerator();
            a = 0;
            while (ienum.MoveNext())
            {
                valoare[a++] = Convert.ToBoolean(ienum.Current);
            }

            for (int i = 0; i <= 3; i++)
            { bits[i] = valoare[i]; }

        }
        public void cu1Op(Instruction instr, bool[] bits)
        {
            System.Collections.IEnumerator ienum;
            Operand o = new Operand();
            o = instr.GetLastOp();
            bool[] valoare = new bool[32];
            switch (o.GetModAdresare())
            {
                case ModAdresare.Imediat: bits[5] = bits[4] = false; index2 = true; opImediat2 = o.GetValImediata(); break;
                case ModAdresare.Indirect: bits[5] = true; bits[4] = false; break;
                case ModAdresare.Direct: bits[5] = false; bits[4] = true; break;
                case ModAdresare.Indexat: bits[5] = bits[4] = true; index2 = true; opImediat2 = o.GetValImediata(); break;
            }
            BitArray nrReg = new BitArray(System.BitConverter.GetBytes(o.Register()));
            ienum = nrReg.GetEnumerator();
            int a = 0;
            while (ienum.MoveNext())
            {
                valoare[a++] = Convert.ToBoolean(ienum.Current);
            }

            for (int i = 0; i <= 3; i++)
            { bits[i] = valoare[i]; }
            
        }
        public void cuBranch(Instruction instr, bool[] bits) 
        {
            Operand o = new Operand();
            o= instr.GetFirstOp();
            switch (o.GetModAdresare())
            {
                case ModAdresare.Imediat: bits[5] = bits[4] = false; index2 = true; opImediat2 = o.GetValImediata(); break;
                case ModAdresare.Indirect: bits[5] = true; bits[4] = false; break;
                case ModAdresare.Direct: bits[5] = false; bits[4] = true; break;
                case ModAdresare.Indexat: bits[5] = bits[4] = true; index2 = true; opImediat2 = o.GetValImediata(); break;
                case ModAdresare.None: eOffset = true; offset = o.GetValImediata(); break;
            }
            if (eOffset) 
            {
                System.Collections.IEnumerator ienum;
                bool[] valoare = new bool[32];
                BitArray bOffset = new BitArray(System.BitConverter.GetBytes(offset));
                ienum = bOffset.GetEnumerator();
                int a = 0;
                while (ienum.MoveNext())
                {
                    valoare[a++] = Convert.ToBoolean(ienum.Current);
                }

                for (int i = 0; i <= 7; i++)
                { bits[i] = valoare[i]; }
 
            }
        }
        //public byte[] BoolToByte(bool[] bools) 
        public Int16 BoolToByte(bool[] bools) 
        {
            Int16 cod = 0;
            Int16 x = 1;
            for (int i = 0; i < bools.Length; i++)
                if (bools[i])
                {
                    //x =(;
                    cod =(short)((int) cod | (short)((int) x << i));
                }   //cod = cod | (x << Convert.ToInt16(i));
            x = Convert.ToInt16(cod);
            return x;
            //int bytes = bools.Length / 8;
            //if ((bools.Length % 8) != 0) bytes++;
            //byte[] arr2 = new byte[bytes];
            //int bitIndex = 0, byteIndex = 0;
            //for (int i = 0; i < bools.Length; i++)
            //{
            //    if (bools[i])
            //    {
            //        arr2[byteIndex] |= (byte)(((byte)1) << bitIndex);
            //    }
            //    bitIndex++;
            //    if (bitIndex == 8)
            //    {
            //        bitIndex = 0;
            //        byteIndex++;
            //    }
            //}
            //return arr2;

        }


    }
}
